<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <script type="text/javascript" src="../../libs/jquery.js"></script>
    <script type="text/javascript" src="../../libs/highlight/highlight.js"></script>
    <script src="../../libs/highlight/languages/cpp.js"></script>
    <link rel="stylesheet" type="text/css" href="../../libs/highlight/styles/vs.css"/>
    <script language="javascript">
        hljs.tabReplace = '    ';
        //hljs.initHighlightingOnLoad();
        hljs.initEnv();
    </script>

    <!--script type="application/x-javascript" src="chrome://global/content/contentAreaUtils.js"></script-->

    <script language="javascript">
    function save(fname, content) {
        try {
            netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
        } catch (e) {
            alert("Permission to save file was denied.");
        }
        var file = Components.classes["@mozilla.org/file/local;1"]
            .createInstance(Components.interfaces.nsILocalFile);

//        var dir = getdir(fname);
//var files = chromeDir.directoryEntries.QueryInterface(Components.interfaces.nsISimpleEnumerator);

        var slash = Components.classes['@mozilla.org/file/directory_service;1'].getService(Components.interfaces.nsIProperties).get("resource:app",Components.interfaces.nsIFile).path;
        if(slash.search(/\\/) != -1)
            fname = fname.replace(/\//g, "\\");

        file.initWithPath( fname );
        if ( file.exists() == false ) {
            alert( "Creating file... " );
            file.create( Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 420 );
        }
        var outputStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
            .createInstance( Components.interfaces.nsIFileOutputStream );
        /* Open flags 
        #define PR_RDONLY       0x01
        #define PR_WRONLY       0x02
        #define PR_RDWR         0x04
        #define PR_CREATE_FILE  0x08
        #define PR_APPEND      0x10
        #define PR_TRUNCATE     0x20
        #define PR_SYNC         0x40
        #define PR_EXCL         0x80
        */
        /*
        ** File modes ....
        **
        ** CAVEAT: 'mode' is currently only applicable on UNIX platforms.
        ** The 'mode' argument may be ignored by PR_Open on other platforms.
        **
        **   00400   Read by owner.
        **   00200   Write by owner.
        **   00100   Execute (search if a directory) by owner.
        **   00040   Read by group.
        **   00020   Write by group.
        **   00010   Execute by group.
        **   00004   Read by others.
        **   00002   Write by others
        **   00001   Execute by others.
        **
        */
        outputStream.init( file, 0x04 | 0x08 | 0x20, 420, 0 );
        var output = content;
        var result = outputStream.write( output, output.length );
        outputStream.close();

        alert("Save successful: `" + fname + "'");
    }

    function savefile( fname, content ) {
        var w = window.frames.w;
        if( !w ) {
            w = document.createElement( 'iframe' );
            w.id = 'w';
            w.style.display = 'none';
            document.body.insertBefore( w, null );
            w = window.frames.w;
            if( !w ) {
                w = window.open( '', '_temp', 'width=800,height=600' );
                if( !w ) {
                    window.alert( 'Sorry, could not create file.' ); return false;
                }
            }
        }
        
        var d = w.document;
        d.open( 'text/plain', 'replace' );
        d.charset = "utf-8";
        d.write( content );
        d.close();

        if( d.execCommand( 'SaveAs', true, fname ) ){
            window.alert( name + ' has been saved.' );
        } else {
            window.alert( 'The file has not been saved.\nIs there a problem?' );
        }
        w.close();
    }
    </script>

</head>
<body>
    <script language="JavaScript">
    var $G;
    var color = ["#9E9EFF", "#FFBBFF", "#A0D5FC", "#FCA0AC", "#A0FCB8", "#DAFBA2",
                 "#FCF8A0", "#FCD5A0", "#FCAAA0" ];
    var xmlfile = "data.xml";
    String.prototype.repeat = function(times){
        return new Array(times+1).join(this);
    }
    String.prototype.trim = function() {
        // this is ok!
        //this = this.replace(/^\s+|\s+$/g,"");
        //return this;
        return this.replace(/^\s+|\s+$/g,"");
    }
    String.prototype.ltrim = function() {
        return this.replace(/^\s+/,"");
    }
    String.prototype.rtrim = function() {
        return this.replace(/\s+$/,"");
    }

    function toxml(div) {
        var func_div = div.clone();
        if(func_div.text()) {
            func_div.find("div").remove();
            var id = func_div.attr("id");
            var level = func_div.attr("level");
            var name = func_div.attr("name");
            var decl = func_div.find("span[xmltag='decl']").text();
            var jdef = func_div.find("span[xmltag='def']");
            jdef.find("a").each(function(){
                var sub_name = $(this).text();
                var sub_ref = $(this).attr("href");
                sub_ref = /.*\(\s*['"](.*)['"].*/.exec(sub_ref);
                sub_ref = sub_ref[1];
                if(sub_name != sub_ref)
                    $(this).replaceWith("__begin_sub__ ref=\"" + sub_ref + "\"__begin_sub_end__" + sub_name + "__end_sub__");
                else
                    $(this).replaceWith("__begin_sub__" + "__begin_sub_end__" + sub_name + "__end_sub__");
            });
            var def = jdef.text().replace(/__begin_sub__/g, "<sub");
            def = def.replace(/__begin_sub_end__/g, ">");
            def = def.replace(/__end_sub__/g, "</sub>");
            return xml(id, level, name, decl, def);
        }
        return "";
    }

    function current() {
        var div = $("#content div");
        var ret = "";
        div.each(function(){
            ret += toxml($(this)) + "\n\n";
        });
        return ret;
    }

    var $ID;
    function show_flow(func, level) {
        if(typeof(func) == "string")
            func = $G.find("func[id='" + func + "']");
        func = func.clone();
        $("#content").empty();
        $ID = $("<flow/>");
        $("#content").append(flow(func, "", level));
        $ID = null;
//        var $div_top = $("#content").find("div[id='" + func.attr("id") + "1" + "']");
        var $div_top = $("#content").find("div").eq(0);
        $div_top.css("margin-left", "0");
//        $div_top.find("div").hide();
//
//        toggle_all("expand", "fast");
    }
    function get_id(id, context) {
        if(!id)
            return "#";
        else {
            if(!context)
                return id;
            var i = 1;
            while(context.find("#" + id + i++).length > 0)
                ;
            var new_id = id + (i - 1);
            context.append('<div id="' + new_id + "></div>");
            return new_id;
        }
    }
    function flow(top, stat, level) {
        // tmporary solution for recursive loop: A->B,B->C,C->A
        if(level > 100)
            return "";

        var ret = "";
        ret += '<span xmltag="decl">' + top.find("decl").text() + '</span>';

        ret += '<span xmltag="def">';
        var marker = "__marker__";
        var parts = top.find("def").contents().wrap("<" + marker + "/>");
        var subs = top.find("def").find(marker);
        subs.each(function(){
            var sub = $(this).find("sub");
            if(sub.text() == "") {
                ret += $(this).text();
            }
            else {
                var sub_id = sub.attr("ref");
                sub_id = sub_id ? sub_id : sub.text();
                var f = $G.find("func[id='" + sub_id + "']").clone();
                var sub_stat = sub.attr("stat");
                stat = stat ? stat : "";
                if(f.text()) {
                    var sub_flow = flow(f, sub_stat, level+1);
                    var new_id = /<div\s+id=['"]([^'"]+)['"]/.exec(sub_flow)[1];
                    if(!new_id)
                        new_id = sub_id;
                    ret += '<b>' +
                        sub.text().link("javascript:toggle_flow(" +
                            "'" + new_id + "'" + ", " +
                            (level+1) +
                            ");") +
                        '</b>';
                    ret += sub_flow;
                }
                else {
                    var new_id = "#";
                    ret += '<b>' +
                        sub.text().link("javascript:toggle_flow(" +
                            "'" + new_id + "'" + ", " +
                            (level+1) +
                            ");") +
                        '</b>';
//                    ret += sub.text();
                }
            }
        });
        ret += '</span>';

//        ret = ret.replace(/\n/gm, "\n    ");

        ret = '<pre style="margin:0;">' + ret + "</pre>";
//        var id = get_id(top.attr("id"), ret);
        var id = top.attr("id");
        id = get_id(id, $ID);
        var xml_level = top.attr("level");
        if(xml_level)
            xml_level = ' level="' + xml_level + '"';
        else
            xml_level = "";
        stat = stat ? stat : "";
        if(stat == "expand")
            stat = "display:block;";
        else if(stat == "collapse")
            stat = "display:none;";
        var div = '<div id="' + id + '"' + xml_level +
            ' name="' + top.find("name").text() + '"' +
            ' style="margin-left:65px;' + 
            'background-color:' + color[level % color.length] + ';' +
            stat +  '">';
        ret = div + ret + '</div>';
        return ret;
    }

    function toggle_flow(func_div, level) {
        var f = $("#" + func_div);
        if(f.css("display") == "none")
            f.show("slow");
        else
            f.hide("slow");
    }

    function toggle_all(action, speed) {
        if(typeof(speed) == "undefined")
            speed = "slow";

        var code_div_top = $("#content div:first");
        if(action == null) {
            if($("#button").attr("name") == "openned")
                action = "collapse";
            else if($("#button").attr("name") == "closed")
                action = "expand";
        }

        if(action == "expand") {
            $("#button").attr("name", "openned");
            $("#button").attr("value", "Collapse All");
            code_div_top.find("div").show(speed);
        }
        else if(action == "collapse") {
            $("#button").attr("name", "closed");
            $("#button").attr("value", "Expand All");
            code_div_top.find("div").hide(speed);
        }
    }

    function save_xml() {
        var filename = $G.find("func[level='top']").eq(0).attr("id");
        filename += '.xml';
        var cur_dir = /^file:\/\/(.*\/).*$/.exec(window.location)[1];
        if(!cur_dir)
            cur_dir = "";
        if(/^\/.:\//.test(cur_dir))
            cur_dir = cur_dir.substring(1);
        filename = cur_dir + filename;
        var content = '<?xml version="1.0" encoding="utf-8" ?>\n' +
            '<flow>' + '\n' +
            $G.html().replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"') +
            '\n' + '</flow>' + '\n';

//        var fso  = new ActiveXObject("Scripting.FileSystemObject");
//        var fh = fso.CreateTextFile("C:\\" + filename, true);
//        fh.WriteLine($G.html());
//        fh.Close();

//        savefile(filename, content);
        save(filename, content);
    }

    function add(tag, attr, attr_val) {
        var jcode = $("#code");
        c = jcode[0];
        if(c && tag) {
            var start = c.selectionStart;
            var end = c.selectionEnd;
            var code = jcode.val();
            var pair = [ "<" + tag + ">", "</" + tag + ">" ];
            if(attr != undefined && attr_val != undefined)
                pair[0] = "<" + tag + " " + attr + '="' + attr_val + '">';
            var ret = code.substring(0, start) + pair[0] +
                code.substring(start, end) + pair[1] +
                code.substring(end);
            $("#code").val(ret);
        }
    }

    function add_decl() {
        var jcode = $("#code");
        c = jcode[0];
        if(c) {
            var start = c.selectionStart;
            var end = c.selectionEnd;
            var code = jcode.val();

            var tag = "decl";
            var pair = [ "<" + tag + ">", "</" + tag + ">" ];
            var ret = pair[0] + code.substring(0, start) + pair[1];

            tag = "def";
            pair = [ "<" + tag + ">", "</" + tag + ">" ];
            ret += pair[0] + code.substring(end) + pair[1];

            $("#code").val(ret);
        }
    }

    function add_sub() {
        var ref = $("#f_ref").val();
        if(ref && ref != "")
            ref = ' ref="' + ref + '"';

        var stat = $("#f_stat").val();
        if(stat && stat != "")
            stat = ' stat="' + stat + '"';

        var ret = "";
        var tag = "sub";
        var pair = [ "<" + tag + ref + stat + ">", "</" + tag + ">" ];

        var jdef = $("#f_def");
        var def = jdef.val();

        var func = $("#f_sub").val();
        if(func && func != "") {
            var re = new RegExp(func, "g");
            ret = def.replace(re, pair[0] + func + pair[1]);
        }
        else {
            var c = jdef[0];
            var start = c.selectionStart;
            var end = c.selectionEnd;
            ret = def.substring(0, start) + pair[0] +
                def.substring(start, end) + pair[1] +
                def.substring(end);
        }

        jdef.val(ret);
    }

    function remove_func() {
        var id = $("#f_id").val();
        id = id ? id.trim() : "";
        if(id) {
            var func = $G.find("func[id='" + id + "']");
            func.remove();
            var $top = $G.find("func[level='top']").clone();
            show_flow($top.eq(0).clone(), 0);
        }
    }

    function edit_func() {
        var id = $("#f_id").val();
        id = id ? id.trim() : "";
        if(id) {
            var func = $G.find("func[id='" + id + "']"); 
            if(func.length) {
                $("#f_id").val(func.attr("id"));
                $("#f_level").val(func.attr("level"));
                $("#f_name").val(func.find("name").text());
                $("#f_decl").val(func.find("decl").text());
                $("#f_def").val(func.find("def").html());
            }
//            func.remove();
//            var $top = $G.find("func[level='top']").clone();
//            show_flow($top.eq(0).clone(), 0);
        }
    }

    function xml(id, level, name, decl, def) {
        var _xml = "";
        if(id && name) {
            var tab = "    ";

            id = id.trim();
            name = name.trim();
            decl = decl ? decl.trim() : "";
            level = level ? level.trim() : "";
            def = def ? def.trim() : "";

            id = ' id=\"' + id + '"';
            level = level ? ' level="' + level + '"' : "";
            _xml += '<func' + id + level + '>';
            _xml += '\n' + tab;

            _xml += '<name>' + name + '</name>';
            _xml += '\n' + tab;

            _xml += '<decl>' + decl + '</decl>';
            _xml += '\n' + tab;

            _xml += '<def>' + '\n';
            _xml += def;
            _xml += '\n' + tab;
            _xml += '</def>';
            _xml += '\n';

            _xml += '</func>' + '\n';
        }
        return _xml;
    }

    function auto_from_decl() {
        var decl = $("#f_decl").val();
        decl = decl ? decl.trim() : "";
        var name = /.*\s+[*]?([0-9a-zA-Z_]+)\s*\(.*/.exec(decl)[1];
        if(name) {
            if(!$("#f_name").val()) {
                $("#f_name").val(name);
                $("#f_name").focus().blur();
            }
        }
    }

    function auto_from_def() {
        var def = $("#f_def").val().trim();
        var decl;
        if(/^#\s*define/.test(def))
            decl = /(.*)\n/.exec(def)[1];
        else
            decl = /([^{]+)\s*[{]?/.exec(def)[1];
        if(decl) {
            decl = decl.trim();
            if(!$("#f_decl").val()) {
                $("#f_decl").val(decl);
                $("#f_decl").focus().blur();
                $("#f_def").val(def.replace(decl, "").trim());
            }
        }
    }

    function genXML() {
        var id = $("#f_id").val();
        var level = $("#f_level").val();
        var name = $("#f_name").val();
        var decl = $("#f_decl").val();
        var def = $("#f_def").val();
        if(id == "")
            id = name;
        if(id && name)
            $("#XMLdata").val(xml(id, level, name, decl, def));
    }

    function submit() {
        var data = $("#XMLdata").val();
        if(data == "") {
            genXML();
            data = $("#XMLdata").val();
        }

        var f = $G.find("func[id='" + $(data).attr("id") + "']");
        if(f.length > 0)
            f.remove();
        $G.append(data);

        var $top = $G.find("func[level='top']").clone();
        show_flow($top.eq(0).clone(), 0);

        clear_text();

        update_head();
    }

    function clear_text() {
        var t = $("#edit table input[type='text']");
        t.each(function(){
            $(this).val("");
        });
        //t = $("#edit table textarea");
        t = $("#edit textarea");
        t.each(function(){
            $(this).val("");
        });
    }

    function func_select(jobj) {
        var c = jobj[0];
        var code = jobj.val();
        var pos = c.selectionStart;
        var i;
        for(i=pos-1; i>=0; i--) {
            if(!/[0-9a-zA-Z_]/.test(code.charAt(i))) {
                c.selectionStart = i + 1;
                break;
            }
        }
        if(i < 0)
            c.selectionStart = 0;
        for(i=pos; i<code.length; i++) {
            if(!/[0-9a-zA-Z_]/.test(code.charAt(i))) {
                c.selectionEnd = i;
                break;
            }
        }
        if(i >= code.length)
            c.selectionEnd = code.length;
    }

    function update_head() {
        $("#head").empty();
        $("#head").append("<h2>Top function list</h2>");
        var $top = $G.find("func[level='top']").clone();
        $("#head").append("<ul/>");
        $top.each(function(){
            var func_id = $(this).attr("id");
            var func_name = $(this).find("name").text();
            func_name = "<li><a href=\"javascript:show_flow('" + func_id + "', 0);\">" + func_name + "</a></li>";
            $("#head ul:first").append(func_name);
        });
        $("#head ul:first").append("<ul/>");
        var $sub = $G.find("func[level='sub']").clone();
        $sub.each(function(){
            var func_id = $(this).attr("id");
            var func_name = $(this).find("name").text();
            func_name = "<li><a href=\"javascript:show_flow('" + func_id + "', 0);\">" + func_name + "</a></li>";
            $("#head ul ul").append(func_name);
        });
    }

    function parse() {
        $.get(xmlfile, function(data){
//            $G.append($("<div/>").append($(data)).find("flow"));
            $G.append($("<div/>").append($(data)).find("flow").children());
            var $h = $("#head");
            var $top = $G.find("func[level='top']").clone();
            update_head();
            $h.append("<br>");

            var $control = $("#control");

            var $c = $("#content");
            show_flow($top.eq(0).clone(), 0);

//            var test = hljs.highlight("cpp", $G.find("func[level='top']").text());
//            test.value = '<pre>' + test.value + '</pre>';
//            $("body").append(test.value);

            var $edit = $("#edit");

        });
    }

    function change_xmlfile() {
        xmlfile = $("#sel").val();
        xmlfile = xmlfile ? xmlfile.trim() : "";
        if(xmlfile) {
            $G = new jQuery("<flow/>");
            parse();
        }
    }

    function get_key(event) {
        if(event.keyCode == 13)
            $("#c_sel").click();
    }

    $(document).ready(function() {
        $G = new jQuery("<flow/>");
        $G.append(current());
        parse();
    });
    </script>

    <!--input type="button" value="current" onclick="current();" /-->

    <div id="xmlsel">
        <label for="sel" >XML data file:</label>
        <input id="sel" type="text" value="data.xml" onkeydown="return get_key(event);" />
        <input id="c_sel" type="button" value="OK" onclick="change_xmlfile();" />
    </div>

    <div id="head">
    </div>

    <div id="control">
        <input id="button" name="closed" type="button" value="Expand All" onclick="javascript:toggle_all();" />
        <input id="buttonSave" type="button" value="SaveTo XML" onclick="javascript:save_xml();" />
    </div>

    <hr>

    <div id="content">
    </div>

    <hr>

    <div id="edit">
        <table cols="2">
            <tbody>
                <tr>
                    <td align="right">id:</td>
                    <td>
                        <input type="text" id="f_id" size="80" onclick="func_select($('#f_id'));" />
                        <input type="button" id="c_id_edit" value="edit" accesskey="e" onclick="edit_func();" />
                        <input type="button" id="c_id_remove" value="remove" accesskey="r" onclick="remove_func();" />
                    </td>
                </tr>
                <tr>
                    <td align="right">level:</td>
                    <!--td><input type="text" id="f_level" size="80" /></td-->
                    <td>
                        <select id="f_level">
                            <option value=""></option>
                            <option value="sub">sub</option>
                            <option value="top">top</option>
                        </select>
                    </td>
                </tr>
                <tr>
                    <td align="right">name:</td>
                    <td><input type="text" id="f_name" size="80" 
                        onclick="func_select($('#f_name'));" 
                        onblur="$('#f_id').val($('#f_name').val());" /></td>
                </tr>
                <tr>
                    <td align="right">decl:</td>
                    <td><input type="text" id="f_decl" size="80" 
                        onclick="func_select($('#f_decl'));" onblur="auto_from_decl();"
                        ondblclick="this.select();" />
                    </td>
                </tr>
                <tr>
                    <td><input type="button" id="c_sub" value="sub" accesskey="s" onclick="add_sub();" /></td>
                    <td>
                        <input type="text" id="f_sub" size="30" onclick="$('#f_sub').select();" />
                        ref:<input type="text" id="f_ref" size="30" onclick="$('#f_ref').select();" />
                        <select id="f_stat">
                            <option value="expand">expand</option>
                            <option value="collapse">collapse</option>
                        </select>
                    </td>
                </tr>
                <tr>
                    <td align="right" valign="top">def:</td>
                    <td><textarea type="textarea" id="f_def" cols="80" rows="20" 
                            onclick="func_select($('#f_def'));"
                            ondblclick="this.select();" 
                            onblur="auto_from_def();"></textarea></td>
                </tr>
            </tbody>
        </table>
        <br>
        <div>
            <input type="button" id="c_gen" value="gen XML" onclick="genXML();" />
            <input type="button" id="c_submit" value="submit" onclick="submit();" style="margin-left:30px;" />
            <input type="button" id="c_clear" value="clear" onclick="clear_text();" />
        </div>
        <div><textarea type="textarea" id="XMLdata" cols="80" rows="20"></textarea></div>
    </div>

    <iframe id="w" style="display: none;">

</body>
</html>
